package com.licode.codekit.buildprj;

import com.licode.codekit.db.AssembledDb;
import com.licode.codekit.db.EntityBuilder;
import com.licode.codekit.db.Table;
import com.licode.codekit.utils.FileUtil;
import com.licode.codekit.utils.LiConfig;
import com.licode.codekit.utils.PomFile;
import com.licode.codekit.utils.StrUtil;
import org.jsoup.internal.StringUtil;

import java.io.File;
import java.util.*;

/**
 * @author licode
 * <p>
 * parent-group-id：父本模块的组ID
 * parent-artifact-id：父模块的artifact-id
 * parent-version：父模块的版本号
 * group-id：本模块的组ID
 * artifact-id：本模块的artifact-id
 * version：模块本身的版本号
 * module-name：模块名称
 * module-class-name：对module-name处理的类名称（按驼峰规则）
 * module-class-name-field：module-class-name第一个字母转小写的属性名
 * artifact-id-pkg：对artifact-id经过转换后包路径名称（去掉-、_和转为小写字母等处理）
 * <p>
 * client-package：client模块完整的包路径
 * service-package：service模块完整的包路径
 * <p>
 * entity-name：数据库表面处理后的实体类名称
 * entity-field：对entity-name处理后的属性名称
 * <p>
 * author：作者
 * date：类生成时间
 * description：类描述
 */
public class ProjectContext {
    private String opType;
    private String rootDir;
    private String currentDir;

    // 当前创建模块的父模块的版本信息
    private String parentGroupId;
    private String parentArtifactId;
    private String parentVersion;

    // 当前创建模块的版本信息
    private String groupId;
    private String artifactId;
    private String artifactIdPkg;
    private String version;
    private String moduleName;
    private String kitVersion;
    private String packaging;

    private String assemblerClassName;

    /**
     * 客户模块的代码目录
     */
    private String clientSrcDir;
    private String serviceSrcDir;

    private String packageDir;
    private String filePackage;

    private String clientPackage;
    private String servicePackage;

    private String poPackage;
    private String entityPackage;
    private String daoPackage;
    private String mapperPackage;
    private String assemblerPackage;

    private String clientModule;
    private String serviceModule;

    private String entitySuffix;
    private boolean hasClient;

    private List<Table> tables;
    private int tablePrefix = 0;
    private String projectDir;

    private boolean onlyEntity = false;

    /**
     * JDK版本
     */
    private int jdkVersion = 8;

    /**
     * 是否是2.x版本的springBoot
     */
    private boolean isSpringBoot2x = true;

    private String author = "licode";

    private String dbUser = "apartment";
    private String dbPwd = "sRbzL1rO3OEHqS^I";
    private String dbUrl = "jdbc:mysql://10.0.14.62:3306/wave_apartment";

    private boolean buildDirWhenGenCode = false;

    private final AssembledDb assembledDb = new AssembledDb();

    private Set<String> modules = new HashSet<>();

    /**
     * 保存子模块包的路径信息（src/main/java开头的路径）
     */
    private List<String> subModulePkgRoot = new ArrayList<>();
    private Map<String, String> cfgModules;

    private final PomFile pomFile = new PomFile();
    private final LiConfig config = new LiConfig();

    public ProjectContext(String opType) {
        this.opType = opType;
    }

    public boolean isOnlyEntity() {
        return onlyEntity;
    }

    public void setOnlyEntity(boolean val) {
        this.onlyEntity = val;
    }

    public int getJdkVersion() {
        return jdkVersion;
    }

    public void setJdkVersion(int jdkVersion) {
        this.jdkVersion = jdkVersion;
    }

    public boolean isSpringBoot2x() {
        return isSpringBoot2x;
    }

    public void setSpringBoot2x(boolean springBoot2x) {
        isSpringBoot2x = springBoot2x;
    }

    public PomFile getPomFile() {
        return this.pomFile;
    }

    public LiConfig getConfig() {
        return this.config;
    }

    public boolean hasConfig() {
        return this.config.isValid();
    }

    public boolean hasPom() {
        return this.pomFile.isValid();
    }

    public boolean parse(String filepath) {
        ProjectContext prjInfo = this;
        String cfgPath;

        prjInfo.getModules().clear();

        projectDir = StrUtil.getFileDir(filepath) + "\\";
        cfgPath = projectDir + "/.idea/li-config.xml";

        pomFile.parse(filepath);

        prjInfo.setGroupId(pomFile.getGroupId());
        prjInfo.setArtifactId(pomFile.getArtifactId());
        prjInfo.setVersion(pomFile.getVersion("0.0.1"));
        prjInfo.setModuleName(prjInfo.getModuleName());

        prjInfo.setParentGroupId(pomFile.getParentGroupId());
        prjInfo.setParentArtifactId(pomFile.getParentArtifactId());
        prjInfo.setParentVersion(pomFile.getParentVersion());

        prjInfo.setModules(pomFile.getModules());
        prjInfo.setPackaging(pomFile.getPackaging());
        prjInfo.setJdkVersion(pomFile.getJdkVersion());

        if (prjInfo.getJdkVersion() > 8) {
            // JDK8以上默认为SpringBoot3.x版本以上
            prjInfo.setSpringBoot2x(false);
        }

        prjInfo.normalize();

        if (!config.parse(cfgPath)) {
            config.createConfig(cfgPath);
        } else {
            cfgModules = config.getModules();
        }

        handleSubModulePkgDir();

        return true;
    }

    public void handleModulePackage() {
        if (config.isValid()) {
            String moduleType = config.getModuleType();
            if (CodeKitConf.MT_SIX_SIDE_PARENT.equals(moduleType) ||
                    CodeKitConf.MT_SIMPLE_PARENT.equals(moduleType)) {
                String cModule = config.getClientModule();
                String sModule = config.getServiceModule();
                this.setClientPackage(config.getClientPackage());
                this.setServicePackage(config.getServicePackage());
                this.setClientSrcDir(projectDir + cModule + "/" + CodeKitConf.SRC_ROOT + packageToPath(getClientPackage()));
                this.setServiceSrcDir(projectDir + sModule + "/" + CodeKitConf.SRC_ROOT + packageToPath(getServicePackage()));
                this.setHasClient(true);
            } else {
                this.setClientPackage(config.getPackage() + "." + CodeKitConf.CLIENT_TAG);
                this.setServicePackage(config.getPackage());
                this.setServiceSrcDir(projectDir + CodeKitConf.SRC_ROOT + packageToPath(getServicePackage()));
                this.setClientSrcDir(projectDir + CodeKitConf.SRC_ROOT + packageToPath(getServicePackage()));
                this.setHasClient(false);
            }
        }
    }

    private void handleSubModulePkgDir() {
        String pkgDir;

        subModulePkgRoot.clear();

        if (CodeKitConf.PACKAGING_JAR.equals(getPackaging())) {
            pkgDir = FileUtil.findDirByServiceTag(new File(projectDir + "/" + CodeKitConf.SRC_ROOT));
            subModulePkgRoot.add(normalizePath(pkgDir));
        } else if (CodeKitConf.PACKAGING_POM.equals(getPackaging())) {
            if (null != cfgModules && cfgModules.size() > 0) {
                for (Map.Entry<String, String> entry : cfgModules.entrySet()) {
                    String name = entry.getKey();
                    String pkg = entry.getValue();
                    if (this.getModules().contains(name)) {
                        pkgDir = projectDir + name + "/" + CodeKitConf.SRC_ROOT + packageToPath(pkg);
                        subModulePkgRoot.add(normalizePath(pkgDir));
                    }
                }
            } else {
                for (String name : this.getModules()) {
                    pkgDir = projectDir + name + "/" + CodeKitConf.SRC_ROOT;
                    pkgDir = FileUtil.findDirByServiceTag(new File(pkgDir));
                    if (!StringUtil.isBlank(pkgDir)) {
                        subModulePkgRoot.add(normalizePath(pkgDir));
                    }
                }
            }
        }
    }

    /**
     * 根据代码的目录前缀找到合适的工程目录
     *
     * @param rootPrefix 前缀
     */
    public String findValidSrcRoot(String rootPrefix) {
        String srcRoot = "";
        String prefix = normalizeRootPrefix(rootPrefix);

        if (CodeKitConf.PACKAGING_JAR.equals(getPackaging())) {
            srcRoot = projectDir + "/" + CodeKitConf.SRC_ROOT;
            srcRoot = FileUtil.findDirByServiceTag(new File(srcRoot));
        } else {
            if (this.cfgModules.size() > 0) {
                srcRoot = findValidDirForConfigModules(prefix);
            } else {
                srcRoot = findValidDirForSubModules(prefix);
            }
        }

        return normalizePath(srcRoot);
    }

    private String normalizeRootPrefix(String rootPrefix) {
        String prefix = rootPrefix;

        if (prefix.startsWith("/")) {
            prefix = prefix.substring(1);
        }
        int pos = prefix.indexOf("/");
        if (-1 != pos) {
            prefix = prefix.substring(0, pos);
        }

        return prefix;
    }

    public String findValidPackage(String rootPrefix) {
        String pkgName = "";
        String fullDir = findValidSrcRoot(rootPrefix);

        if (!StringUtil.isBlank(fullDir)) {
            int pos = fullDir.indexOf(CodeKitConf.SRC_ROOT);
            if (-1 != pos) {
                pkgName = fullDir.substring(pos + CodeKitConf.SRC_ROOT.length());
                pkgName = handlePackage(pkgName);
            }
        }

        return pkgName;
    }

    private String findValidDirForConfigModules(String prefix) {
        return this.findValidDirForSubModules(prefix);
    }

    private String findValidDirForSubModules(String prefix) {
        String srcRoot = "";

        for (String item : subModulePkgRoot) {
            if (item.endsWith("/" + prefix)) {
                srcRoot = item.substring(0, item.length() - prefix.length());
                break;
            } else if (item.endsWith("/" + prefix + "/")) {
                srcRoot = item.substring(0, item.length() - prefix.length() - 1);
                break;
            } else if (FileUtil.pathExist(item + prefix)) {
                srcRoot = item;
                break;
            }
        }

        return srcRoot;
    }

    private String packageToPath(String pkg) {
        return pkg.replace(".", "/") + "/";
    }

    public void addModule(String moduleName) {
        pomFile.addModule(moduleName);
    }

    public void updatePomFile() {
        pomFile.save();
    }

    public boolean isPom() {
        return CodeKitConf.PACKAGING_POM.equals(this.packaging);
    }

    public boolean isJar() {
        return CodeKitConf.PACKAGING_JAR.equals(this.packaging);
    }

    public boolean canAddModule() {
        if (!isPom()) {
            return false;
        } else {
            String moduleType = getConfig().getModuleType();
            if (CodeKitConf.MT_SPEC_SIX.equals(moduleType) ||
                    CodeKitConf.MT_SINGLE_SIX.equals(moduleType) ||
                    CodeKitConf.MT_SIMPLE_ARCH.equals(moduleType)) {
                return false;
            } else {
                return true;
            }
        }
    }

    public boolean isCanGenerateCode() {
        String moduleType = getConfig().getModuleType();
        if (isPom()) {
            return CodeKitConf.MT_SPEC_SIX.equals(moduleType) ||
                    CodeKitConf.MT_SINGLE_SIX.equals(moduleType) ||
                    CodeKitConf.MT_SIMPLE_ARCH.equals(moduleType);
        } else if (isJar()) {
            if (moduleType.startsWith("single-six-") ||
                    moduleType.startsWith("simple-arch-")) {
                return false;
            } else {
                return !moduleType.startsWith("spec-six-");
            }
        } else {
            return false;
        }
    }

    public String getEntityName(Table table) {
        return EntityBuilder.javaClassName(table.getName(), tablePrefix);
    }

    public String getPrimaryType(Table table) {
        String type = EntityBuilder.javaType(table.getPrimaryCol().getType());
        if ("String".equals(type)) {
            return "Str";
        }
        return "Int";
    }

    public boolean isBuildDirWhenGenCode() {
        return buildDirWhenGenCode;
    }

    public void setBuildDirWhenGenCode(boolean buildDirWhenGenCode) {
        this.buildDirWhenGenCode = buildDirWhenGenCode;
    }

    public String getAssemblerClassName() {
        return assemblerClassName;
    }

    public String getClientModule() {
        return clientModule == null ? "" : clientModule;
    }

    public void setClientModule(String clientModule) {
        this.clientModule = clientModule;
    }

    public String getServiceModule() {
        return serviceModule == null ? "" : serviceModule;
    }

    public void setServiceModule(String serviceModule) {
        this.serviceModule = serviceModule;
    }

    public void setAssemblerClassName(String assemblerClassName) {
        this.assemblerClassName = assemblerClassName;
    }

    public String getParentGroupId() {
        return parentGroupId == null ? "" : parentGroupId;
    }

    public void setParentGroupId(String parentGroupId) {
        this.parentGroupId = parentGroupId;
    }

    public String getParentArtifactId() {
        return parentArtifactId == null ? "" : parentArtifactId;
    }

    public void setParentArtifactId(String parentArtifactId) {
        this.parentArtifactId = parentArtifactId;
    }

    public String getArtifactIdPkg() {
        return artifactIdPkg == null ? "" : artifactIdPkg;
    }

    public int getTablePrefix() {
        return tablePrefix;
    }

    public void setTablePrefix(int tablePrefix) {
        this.tablePrefix = tablePrefix;
    }

    public AssembledDb getAssembledDb() {
        return assembledDb;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getDbUser() {
        return dbUser;
    }

    public void setDbUser(String dbUser) {
        this.dbUser = dbUser;
    }

    public String getDbPwd() {
        return dbPwd;
    }

    public void setDbPwd(String dbPwd) {
        this.dbPwd = dbPwd;
    }

    public String getDbUrl() {
        return dbUrl;
    }

    public void setDbUrl(String dbUrl) {
        this.dbUrl = dbUrl;
    }

    public List<Table> getTables() {
        return tables;
    }

    public void setTables(List<Table> tables) {
        this.tables = tables;
    }

    public String getRootDir() {
        return rootDir;
    }

    public void setRootDir(String rootDir) {
        this.rootDir = rootDir;
    }

    public String getCurrentDir() {
        return currentDir;
    }

    public void setCurrentDir(String currentDir) {
        this.currentDir = currentDir;
    }

    public String getModuleName() {
        return moduleName == null ? getArtifactId() : moduleName;
    }

    public String getModuleClassName() {
        return StrUtil.firstUpper(this.getModuleName());
    }

    public String getModuleClassNameField() {
        return StrUtil.makeFirstLower(this.getModuleClassName());
    }

    public void setModuleName(String moduleName) {
        this.moduleName = moduleName.toLowerCase();
    }

    public String getGroupId() {
        return groupId;
    }

    public void setGroupId(String groupId) {
        this.groupId = groupId.toLowerCase().trim();
        setPackageDir(getGroupId().replace(".", "/"));
    }

    public String getArtifactId() {
        return artifactId == null ? "" : artifactId;
    }

    public void setArtifactId(String artifactId) {
        this.artifactId = artifactId.toLowerCase();
        this.artifactIdPkg = StrUtil.cvModuleName(artifactId).toLowerCase();
    }

    public String getPackaging() {
        return packaging;
    }

    public void setPackaging(String packaging) {
        this.packaging = packaging;
    }

    public String getVersion() {
        return version == null ? "0.0.1" : version;
    }

    public String getConfigVersion() {
        return "1.0.0";
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getParentVersion() {
        return parentVersion == null ? "0.0.1.parent" : parentVersion;
    }

    public void setParentVersion(String version) {
        this.parentVersion = version;
    }

    public String getKitVersion() {
        return kitVersion;
    }

    public void setKitVersion(String kitVersion) {
        this.kitVersion = kitVersion;
    }

    public boolean isHasClient() {
        return hasClient;
    }

    public void setHasClient(boolean hasClient) {
        this.hasClient = hasClient;
    }

    public Set<String> getModules() {
        return modules;
    }

    public void setModules(Set<String> modules) {
        this.modules = modules;
    }

    public String getClientSrcDir() {
        return clientSrcDir;
    }

    public void setClientSrcDir(String clientSrcDir) {
        this.clientSrcDir = normalizePath(clientSrcDir);
    }

    public String getServiceSrcDir() {
        return serviceSrcDir;
    }

    public void setServiceSrcDir(String serviceSrcDir) {
        this.serviceSrcDir = normalizePath(serviceSrcDir);
    }

    public String normalizePath(String strPath) {
        return strPath.replace("\\", "/")
                .replace("//", "/");
    }

    public String getFilePackage() {
        return filePackage == null ? "" : filePackage;
    }

    public void setFilePackage(String filePackage) {
        this.filePackage = handlePackage(filePackage);
    }

    public String getPackageDir() {
        return packageDir == null ? "" : packageDir;
    }

    public void setPackageDir(String packageDir) {
        this.packageDir = normalizePath(packageDir);
    }

    public String getClientPackage() {
        return clientPackage == null ? "" : clientPackage;
    }

    public void setClientPackage(String clientPackage) {
        this.clientPackage = handlePackage(clientPackage);
    }

    public String getServicePackage() {
        return servicePackage == null ? "" : servicePackage;
    }

    public void setServicePackage(String servicePackage) {
        this.servicePackage = handlePackage(servicePackage);
    }

    public String getPoPackage() {
        return poPackage == null ? "" : poPackage;
    }

    public void setPoPackage(String poPackage) {
        this.poPackage = handlePackage(poPackage);
    }

    public String getEntityPackage() {
        return entityPackage == null ? "" : entityPackage;
    }

    public void setEntityPackage(String entityPackage) {
        this.entityPackage = handlePackage(entityPackage);
    }

    public String getDaoPackage() {
        return daoPackage == null ? "" : daoPackage;
    }

    public void setDaoPackage(String daoPackage) {
        this.daoPackage = handlePackage(daoPackage);
    }

    public String getMapperPackage() {
        return mapperPackage == null ? "" : mapperPackage;
    }

    public void setMapperPackage(String mapperPackage) {
        this.mapperPackage = handlePackage(mapperPackage);
    }

    public String getAssemblerPackage() {
        return assemblerPackage == null ? "" : assemblerPackage;
    }

    public void setAssemblerPackage(String assemblerPackage) {
        this.assemblerPackage = handlePackage(assemblerPackage);
    }

    public String getEntitySuffix() {
        return entitySuffix == null ? "" : entitySuffix;
    }

    public void setEntitySuffix(String entitySuffix) {
        this.entitySuffix = entitySuffix;
    }

    private String handlePackage(String pkg) {
        String retPkg = "";
        if (pkg != null) {
            retPkg = pkg.replace("/", ".")
                    .replace("\\", ".");

            if (retPkg.startsWith(".")) {
                retPkg = retPkg.substring(1);
            }
            if (retPkg.endsWith(".")) {
                retPkg = retPkg.substring(0, retPkg.length() - 1);
            }
        }
        return retPkg;
    }

    public void normalize() {
        if (null == moduleName) {
            moduleName = artifactId;
        }

        if (StringUtil.isBlank(this.getGroupId()) &&
                this.opType.equals(CodeKitConf.CODE_TYPE)) {
            this.groupId = this.parentGroupId;
        }

        /**
         * 只有生成代码的时候才检测版本号
         */
        if (StringUtil.isBlank(this.getVersion()) &&
                this.opType.equals(CodeKitConf.CODE_TYPE)) {
            this.version = this.parentVersion;
        }

        if (null == parentVersion) {
            parentVersion = "0.0.1.parent";
        }

        /**
         * 对于增加模块来说，当前模块就是增加模块的父模块
         * 所以，需要进行处理
         */
        if (this.opType.equals(CodeKitConf.MODULE_TYPE)) {
            if (!StringUtil.isBlank(this.getGroupId())) {
                this.parentGroupId = this.getGroupId();
            }

            this.parentArtifactId = this.getArtifactId();

            if (!StringUtil.isBlank(this.version)) {
                this.parentVersion = this.version;
            }

            this.setArtifactId("");
            this.setModuleName(this.artifactId);
            this.setVersion("0.0.1");
        }
    }

    private String getPackage(int projectMode) {
        if (CodeKitConf.CLIENT_PRJ == projectMode) {
            return this.clientPackage;
        } else if (CodeKitConf.SERVICE_PRJ == projectMode) {
            return this.servicePackage;
        } else {
            return "";
        }
    }

    public String handleContent(String content) {
        return this.handleContent(content, 0);
    }

    public String handleContent(String content, int projectMode) {
        String retContent;
        String jdkVersion="1.8";

        if (this.isSpringBoot2x) {
            retContent = content.replace("{dao-resource}", "javax.annotation.Resource")
                    .replace("{mybatis-plus-boot-starter}", "mybatis-plus-boot-starter")
                    .replace("{spring-boot-version}", "2.3.12.RELEASE")
                    .replace("{spring-cloud-version}", "Hoxton.SR12")
                    .replace("{alibaba-cloud-version}", "2.2.7.RELEASE");
        } else {
            retContent = content.replace("{dao-resource}", "jakarta.annotation.Resource")
                    .replace("{mybatis-plus-boot-starter}", "mybatis-plus-spring-boot3-starter")
                    .replace("{spring-boot-version}", "3.2.6")
                    .replace("{spring-cloud-version}", "2023.0.1")
                    .replace("{alibaba-cloud-version}", "2023.0.1.0");
        }

        if (this.getJdkVersion() > 8) {
            jdkVersion = this.getJdkVersion() + "";
        }

        return retContent.replace("{author}", this.getAuthor())
                .replace("{description}", getModuleName())
                .replace("{date}", StrUtil.nowTime())
                .replace("{parent-group-id}", getParentGroupId())
                .replace("{parent-artifact-id}", getParentArtifactId())
                .replace("{parent-version}", getParentVersion())
                .replace("{group-id}", getGroupId())
                .replace("{package}", getGroupId())
                .replace("{file-package}", getFilePackage())
                .replace("{artifact-id}", getArtifactId())
                .replace("{artifact-id-pkg}", getArtifactIdPkg())
                .replace("{version}", getVersion())
                .replace("{config-version}", getConfigVersion())
                .replace("{entity-suffix}", getEntitySuffix())
                .replace("{package}", getPackage(projectMode))
                .replace("{client-module}", getClientModule())
                .replace("{service-module}", getServiceModule())
                .replace("{module-name}", getModuleName())
                .replace("{module-class-name}", getModuleClassName())
                .replace("{module-class-name-field}", getModuleClassNameField())
                .replace("{po-package}", this.getPoPackage())
                .replace("{entity-package}", this.getEntityPackage())
                .replace("{mapper-package}", this.getMapperPackage())
                .replace("{dao-package}", this.getDaoPackage())
                .replace("{assembler-package}", this.getAssemblerPackage())
                .replace("{client-package}", this.getClientPackage())
                .replace("{service-package}", this.getServicePackage())
                .replace("{jdk-version}", jdkVersion);
    }
}
